package cn.t.keycloak.plugin.login.form;

import cn.t.keycloak.plugin.login.AuthConstants;
import cn.t.keycloak.plugin.login.util.SmsCache;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;
import org.keycloak.models.*;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 手机验证码登录表单(兼容账户密码登录)
 * @author 陶敏麒
 * @date 2023/11/22 9:25
 */
@Slf4j
public class PhoneLoginFormAuthenticator extends UsernamePasswordForm implements Authenticator, AuthenticatorFactory {

    private static final String PROVIDER_ID = "phone-login-form";

    /**
     * 重写这个方法即可,前面的使用默认的就行(利用主题修改前端表单)
     */
    @Override
    protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
        // 判断是否是手机登录
        boolean byPhone = Objects.equals(formData.getFirst("phoneActivated"), "1");
        if (byPhone) {
            String regionCode = formData.getFirst("regionCode");
            context.form().setAttribute("regionCode", regionCode);
            context.form().setAttribute("phoneActivated", formData.getFirst("phoneActivated"));
            String phone = formData.getFirst("phone");
            if (phone == null || phone.isBlank()) {
                Response challengeResponse = super.challenge(context, "手机号不能为空", "phone");
                context.forceChallenge(challengeResponse);
                return false;
            }
            // String phoneNumber = formData.getFirst(AuthConstants.FIELD_PHONE_NUMBER);
            String phoneNumber = regionCode + phone;
            // 保留输入框的内容
            context.form().setAttribute("phone", phone);
            context.form().setAttribute(AuthConstants.FIELD_PHONE_NUMBER, phoneNumber);

            // 校验验证码
            String code = formData.getFirst(AuthConstants.FIELD_VALIDATE_CODE);
            if (!Objects.equals(code, SmsCache.getCode(context.getRealm().getName(), phoneNumber))) {
                // 验证码字段报错
                Response challengeResponse = super.challenge(context, "验证码不正确", AuthConstants.FIELD_VALIDATE_CODE);
                context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
                return false;
            }
            // 我也不知为什么,官方是这么写的
            context.clearUser();
            // 校验手机号是否存在
            UserProvider userProvider = context.getSession().users();
            SmsCache.cleanCode(context.getRealm().getName(), phoneNumber);
            var opUser = userProvider.searchForUserByUserAttributeStream(context.getRealm(), AuthConstants.FIELD_PHONE_NUMBER, phoneNumber).findFirst();
            boolean result = opUser.isPresent();
            if (result) {
                // 手机登录的,后续无需再二次验证了
                context.getAuthenticationSession().setAuthNote(AuthConstants.SKIP_SMS_MFA, "1");
                context.setUser(opUser.get());
            } else {
                context.getEvent().user("");
                context.getEvent().error("invalid_user_credentials");
                Response challengeResponse = this.challenge(context, this.getDefaultChallengeMessage(context), "password");
                context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
            }
            return result;
        } else {
            context.getAuthenticationSession().setAuthNote(AuthConstants.SKIP_SMS_MFA, "0");
            // 走默认的校验
            return super.validateForm(context, formData);
        }
    }

    @Override
    public String getDisplayType() {
        return "自定义手机号码登录(兼容密码登录)";
    }

    @Override
    public String getReferenceCategory() {
        return PasswordCredentialModel.TYPE;
    }

    @Override
    public boolean isConfigurable() {
        return false;
    }

    /**
     * 表单登录的必须设置为必须
     */
    public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
            AuthenticationExecutionModel.Requirement.REQUIRED
    };
    @Override
    public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
        return REQUIREMENT_CHOICES;
    }

    @Override
    public boolean isUserSetupAllowed() {
        return false;
    }

    // 显示文本
    @Override
    public String getHelpText() {
        return "自定义手机号码登录(兼容密码登录)";
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        return new ArrayList<>();
    }

    @Override
    public Authenticator create(KeycloakSession keycloakSession) {
        return this;
    }

    @Override
    public void init(Config.Scope scope) { log.info(">>>>自定义的登录表单开始加载"); }

    @Override
    public void postInit(KeycloakSessionFactory keycloakSessionFactory) {}

    @Override
    public String getId() {
        return PROVIDER_ID;
    }
}
